<template>
  <h1>Child</h1>
  {{ data }}
  <hr />
  <button @click="dataChange">change</button>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'

// 父组件向子组件传值：自定义属性
// 在父组件的子组件元素上声明自定义属性，将自定义属性值绑定为需要传递的值
// 在子组件中使用泛型接口形式调用 defineProps 方法，接受自定义属性传递的值
// 该泛型接口需要额外定义，接口包含自定义属性名和类型
// 使用泛型接口形式调用后，可以直接在 html 模板中
// 在接口中使用 ? 来声明非必要的自定义属性
// interface Props {
//   val?: type
// }
// defineProps 方法返回值为 props 对象，可以通过 props 对象访问并使用自定义属性传来的数据或方法
// const props = defineProps<Props>()
// withDefaults 方法，第一个参数为 props 对象，第二个参数为配置对象
// 在配置对象中，可以对自定义属性设置默认值，当父组件未传递值时，为默认值
// withDefaults(props, { val: defaultVal })

// 子组件向父组件传值：自定义事件
// 在子组件中注册自定义事件，并得到自定义事件的触发函数
// defineEmits 参数为字符串数组，以对应字符串为并注册自定义事件
// 返回值为自定义事件的触发函数
// const emits = defineEmits(['name1', 'name2'])
// 在子组件中调用触发函数，并将数据以参数形式传递
// emits 触发函数，第一个参数为注册的自定义事件名，第二个参数为需传递给父组件的数据
// emits('name1', ...val)
// 在父组件中的子组件元素上绑定注册的自定义事件，并设置对应的事件处理函数
// 在父组件的自定义事件处理函数中，可以以参数形式接受到子组件的传值
// 实质：子组件注册自定义事件，父组件的子组件元素绑定自定义事件，并设置事件处理函数
// 子组件触发自定义事件，传递参数 => 父组件触发自定义事件处理函数，接受参数

// 子组件向父组件暴露值：父组件获取子组件元素形式
// defineExpose 参数为对象，对象上挂载数据，将挂载数据项父组件暴露
// defineExpose({ val })
// 父组件中使用 ref 获取子组件元素，可以通过子组件 DOM 来访问暴露的数据

// 定义约束自定义属性对象的接口
interface Props {
  // 使用 ? 来声明为非必要的自定义属性
  data?: {
    name: string
  }
  dataChange?: any
}
// 调用 withDefaults 方法，第一个参数为 defineProps 返回值，第二个参数为对象
// 在对象内可以设置自定义属性的默认值，对于引用类型，需要定义在回调函数的返回值上
// defineProps 方法，使用泛型调用，可以直接接受父组件自定义传递的值
// 由于在接口定义且使用泛型，可以直接在 html 模板使用
withDefaults(defineProps<Props>(), {
  data: () => {
    return { name: 'Jerry' }
  },
})

const childData1 = reactive({
  name: 'Jerry',
})
const childData2 = ref('111')

// defineEmits 参数为字符串数组，即根据数组名注册若干个自定义事件
// 返回值为自定义事件的触发函数 emits
const emits = defineEmits(['cus1', 'cus2'])
// 调用自定义事件的触发函数 emits
// 第一个参数为自定义事件名，第二个参数为向自定义事件中传递的数据
emits('cus1', childData1, false)
emits('cus2', childData2)
// defineExpose 参数为对象
// 将子组件的数据挂载到该参数对象上时
// 父组件可以通过 ref 引用子组件 DOM 来获取挂载的数据
defineExpose({
  childData1,
  childData2,
})
</script>
<style scoped></style>
